{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "1Z6Wtb_jisbA"
      },
      "source": [
        "##### Copyright 2019 The TensorFlow Authors."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "cellView": "form",
        "id": "QUyRGn9riopB"
      },
      "outputs": [],
      "source": [
        "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n",
        "# you may not use this file except in compliance with the License.\n",
        "# You may obtain a copy of the License at\n",
        "#\n",
        "# https://www.apache.org/licenses/LICENSE-2.0\n",
        "#\n",
        "# Unless required by applicable law or agreed to in writing, software\n",
        "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
        "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
        "# See the License for the specific language governing permissions and\n",
        "# limitations under the License."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "H1yCdGFW4j_F"
      },
      "source": [
        "# 사전 제작 Estimator"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "PS6_yKSoyLAl"
      },
      "source": [
        "<table class=\"tfo-notebook-buttons\" align=\"left\">\n",
        "  <td><a target=\"_blank\" href=\"https://www.tensorflow.org/tutorials/estimator/premade\"><img src=\"https://www.tensorflow.org/images/tf_logo_32px.png\">TensorFlow.org에서 보기</a></td>\n",
        "  <td><a target=\"_blank\" href=\"https://colab.research.google.com/github/tensorflow/docs/blob/master/site/en/tutorials/estimator/premade.ipynb\"><img src=\"https://www.tensorflow.org/images/colab_logo_32px.png\">Google Colab에서 실행하기</a></td>\n",
        "  <td><a target=\"_blank\" href=\"https://github.com/tensorflow/docs/blob/master/site/en/tutorials/estimator/premade.ipynb\"><img src=\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\">GitHub에서소스 보기</a></td>\n",
        "  <td><a href=\"https://storage.googleapis.com/tensorflow_docs/docs/site/en/tutorials/estimator/premade.ipynb\"><img src=\"https://www.tensorflow.org/images/download_logo_32px.png\">노트북 다운로드하기</a></td>\n",
        "</table>"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "R4YZ_ievcY7p"
      },
      "source": [
        "이 튜토리얼에서는 Estimator를 사용하여 TensorFlow에서 Iris 분류 문제를 해결하는 방법을 보여줍니다. Estimator는 완전한 모델을 TensorFlow에서 높은 수준으로 표현한 것이며, 간편한 크기 조정과 비동기식 훈련에 목적을 두고 설계되었습니다. 자세한 내용은 [Estimator](https://www.tensorflow.org/guide/estimator)를 참조하세요.\n",
        "\n",
        "TensorFlow 2.0에서 [Keras API](https://www.tensorflow.org/guide/keras)는 이러한 작업을 상당 부분 동일하게 수행할 수 있으며 배우기 쉬운 API로 여겨집니다. 새로 시작하는 경우 Keras로 시작하는 것이 좋습니다. TensorFlow 2.0에서 사용 가능한 고급 API에 대한 자세한 정보는 [Keras에 표준화](https://medium.com/tensorflow/standardizing-on-keras-guidance-on-high-level-apis-in-tensorflow-2-0-bad2b04c819a)를 참조하세요.\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "8IFct0yedsTy"
      },
      "source": [
        "## 시작을 위한 준비\n",
        "\n",
        "시작하려면 먼저 TensorFlow와 필요한 여러 라이브러리를 가져옵니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "jPo5bQwndr9P"
      },
      "outputs": [],
      "source": [
        "import tensorflow as tf\n",
        "\n",
        "import pandas as pd"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "c5w4m5gncnGh"
      },
      "source": [
        "## 데이터세트\n",
        "\n",
        "이 문서의 샘플 프로그램은 아이리스 꽃을 [꽃받침잎](https://en.wikipedia.org/wiki/Sepal)과 [꽃잎](https://en.wikipedia.org/wiki/Petal)의 크기에 따라 세 가지 종으로 분류하는 모델을 빌드하고 테스트합니다.\n",
        "\n",
        "Iris 데이터세트를 사용하여 모델을 훈련합니다. Iris 데이터세트에는 네 가지 특성과 하나의 [레이블](https://developers.google.com/machine-learning/glossary/#label)이 있습니다. 이 네 가지 특성은 개별 아이리스 꽃의 다음과 같은 식물 특성을 식별합니다.\n",
        "\n",
        "- 꽃받침잎 길이\n",
        "- 꽃받침잎 너비\n",
        "- 꽃잎 길이\n",
        "- 꽃잎 너비\n",
        "\n",
        "이 정보를 바탕으로 데이터를 구문 분석하는 데 도움이 되는 몇 가지 상수를 정의할 수 있습니다.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "lSyrXp_He_UE"
      },
      "outputs": [],
      "source": [
        "CSV_COLUMN_NAMES = ['SepalLength', 'SepalWidth', 'PetalLength', 'PetalWidth', 'Species']\n",
        "SPECIES = ['Setosa', 'Versicolor', 'Virginica']"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "j6mTfIQzfC9w"
      },
      "source": [
        "그 다음, Keras 및 Pandas를 사용하여 Iris 데이터세트를 다운로드하고 구문 분석합니다. 훈련 및 테스트를 위해 별도의 데이터세트를 유지합니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "PumyCN8VdGGc"
      },
      "outputs": [],
      "source": [
        "train_path = tf.keras.utils.get_file(\n",
        "    \"iris_training.csv\", \"https://storage.googleapis.com/download.tensorflow.org/data/iris_training.csv\")\n",
        "test_path = tf.keras.utils.get_file(\n",
        "    \"iris_test.csv\", \"https://storage.googleapis.com/download.tensorflow.org/data/iris_test.csv\")\n",
        "\n",
        "train = pd.read_csv(train_path, names=CSV_COLUMN_NAMES, header=0)\n",
        "test = pd.read_csv(test_path, names=CSV_COLUMN_NAMES, header=0)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "wHFxNLszhQjz"
      },
      "source": [
        "데이터를 검사하여 네 개의 float 특성 열과 하나의 int32 레이블이 있는지 확인할 수 있습니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "WOJt-ML4hAwI"
      },
      "outputs": [],
      "source": [
        "train.head()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "jQJEYfVvfznP"
      },
      "source": [
        "각 데이터세트에 대해 예측하도록 모델을 훈련할 레이블을 분할합니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "zM0wz2TueuA6"
      },
      "outputs": [],
      "source": [
        "train_y = train.pop('Species')\n",
        "test_y = test.pop('Species')\n",
        "\n",
        "# The label column has now been removed from the features.\n",
        "train.head()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "jZx1L_1Vcmxv"
      },
      "source": [
        "## Estimator를 사용한 프로그래밍 개요\n",
        "\n",
        "이제 데이터가 설정되었으므로 TensorFlow Estimator를 사용하여 모델을 정의할 수 있습니다. Estimator는 `tf.estimator.Estimator`에서 파생된 임의의 클래스입니다. TensorFlow는 일반적인 ML 알고리즘을 구현하기 위해 `tf.estimator`(예: `LinearRegressor`) 모음을 제공합니다. 그 외에도 고유한 [사용자 정의 Estimator](https://www.tensorflow.org/guide/custom_estimators)를 작성할 수 있습니다. 처음 시작할 때는 사전 제작된 Estimator를 사용하는 것이 좋습니다.\n",
        "\n",
        "사전 제작된 Estimator를 기초로 TensorFlow 프로그램을 작성하려면 다음 작업을 수행해야 합니다.\n",
        "\n",
        "- 하나 이상의 입력 함수를 작성합니다.\n",
        "- 모델의 특성 열을 정의합니다.\n",
        "- 특성 열과 다양한 하이퍼 매개변수를 지정하여 Estimator를 인스턴스화합니다.\n",
        "- Estimator 객체에서 하나 이상의 메서드를 호출하여 적합한 입력 함수를 데이터 소스로 전달합니다.\n",
        "\n",
        "이러한 작업이 Iris 분류를 위해 어떻게 구현되는지 알아보겠습니다."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "2OcguDfBcmmg"
      },
      "source": [
        "## 입력 함수 작성하기\n",
        "\n",
        "훈련, 평가 및 예측을 위한 데이터를 제공하려면 입력 함수를 작성해야 합니다.\n",
        "\n",
        "**입력 함수**는 다음 두 요소 튜플을 출력하는 `tf.data.Dataset` 객체를 반환하는 함수입니다.\n",
        "\n",
        "- [`features`](https://developers.google.com/machine-learning/glossary/#feature) -다음과 같은 Python 사전:\n",
        "    - 각 키가 특성의 이름입니다.\n",
        "    - 각 값은 해당 특성 값을 모두 포함하는 배열입니다.\n",
        "- `label` - 모든 예제의 [레이블](https://developers.google.com/machine-learning/glossary/#label) 값을 포함하는 배열입니다.\n",
        "\n",
        "입력 함수의 형식을 보여주기 위해 여기에 간단한 구현을 나타냈습니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "nzr5vRr5caGF"
      },
      "outputs": [],
      "source": [
        "def input_evaluation_set():\n",
        "    features = {'SepalLength': np.array([6.4, 5.0]),\n",
        "                'SepalWidth':  np.array([2.8, 2.3]),\n",
        "                'PetalLength': np.array([5.6, 3.3]),\n",
        "                'PetalWidth':  np.array([2.2, 1.0])}\n",
        "    labels = np.array([2, 1])\n",
        "    return features, labels"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "NpXvGjfnjHgY"
      },
      "source": [
        "입력 함수에서 원하는 대로 `features` 사전 및 `label` 목록이 생성되도록 할 수 있습니다. 그러나 모든 종류의 데이터를 구문 분석할 수 있는 TensorFlow의 [Dataset API](https://www.tensorflow.org/guide/datasets)를 사용하는 것이 좋습니다.\n",
        "\n",
        "Dataset API는 많은 일반적인 경우를 자동으로 처리할 수 있습니다. 예를 들어, Dataset API를 사용하면 대규모 파일 모음에서 레코드를 병렬로 쉽게 읽고 이를 단일 스트림으로 결합할 수 있습니다.\n",
        "\n",
        "이 예제에서는 작업을 단순화하기 위해 [pandas](https://pandas.pydata.org/) 데이터를 로드하고 이 인메모리 데이터에서 입력 파이프라인을 빌드합니다.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "T20u1anCi8NP"
      },
      "outputs": [],
      "source": [
        "def input_fn(features, labels, training=True, batch_size=256):\n",
        "    \"\"\"An input function for training or evaluating\"\"\"\n",
        "    # Convert the inputs to a Dataset.\n",
        "    dataset = tf.data.Dataset.from_tensor_slices((dict(features), labels))\n",
        "\n",
        "    # Shuffle and repeat if you are in training mode.\n",
        "    if training:\n",
        "        dataset = dataset.shuffle(1000).repeat()\n",
        "    \n",
        "    return dataset.batch(batch_size)\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "xIwcFT4MlZEi"
      },
      "source": [
        "## 특성 열 정의하기\n",
        "\n",
        "[**특성 열**](https://developers.google.com/machine-learning/glossary/#feature_columns)은 모델이 특성 사전의 원시 입력 데이터를 사용하는 방식을 설명하는 객체입니다. Estimator 모델을 빌드할 때는 모델에서 사용할 각 특성을 설명하는 특성 열 목록을 전달합니다. `tf.feature_column` 모듈은 모델에 데이터를 나타내기 위한 많은 옵션을 제공합니다.\n",
        "\n",
        "Iris의 경우 4개의 원시 특성은 숫자 값이므로, 네 개의 특성 각각을 32-bit 부동 소수점 값으로 나타내도록 Estimator 모델에 알려주는 특성 열 목록을 빌드합니다. 따라서 특성 열을 작성하는 코드는 다음과 같습니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "ZTTriO8FlSML"
      },
      "outputs": [],
      "source": [
        "# Feature columns describe how to use the input.\n",
        "my_feature_columns = []\n",
        "for key in train.keys():\n",
        "    my_feature_columns.append(tf.feature_column.numeric_column(key=key))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "jpKkhMoZljco"
      },
      "source": [
        "특성 열은 여기에 표시된 것보다 훨씬 정교할 수 있습니다. [이 가이드](https://www.tensorflow.org/guide/feature_columns)에서 특성 열에 대한 자세한 내용을 읽을 수 있습니다.\n",
        "\n",
        "모델이 원시 특성을 나타내도록 할 방식에 대한 설명이 준비되었으므로 Estimator를 빌드할 수 있습니다."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "kuE59XHEl22K"
      },
      "source": [
        "## Estimator 인스턴스화하기\n",
        "\n",
        "Iris 문제는 고전적인 분류 문제입니다. 다행히도 TensorFlow는 다음을 포함하여 여러 가지 사전 제작된 분류자 Estimator를 제공합니다.\n",
        "\n",
        "- 다중 클래스 분류를 수행하는 심층 모델을 위한 `tf.estimator.DNNClassifier`\n",
        "- 넓고 깊은 모델을 위한 `tf.estimator.DNNLinearCombinedClassifier`\n",
        "- 선형 모델에 기초한 분류자를 위한 `tf.estimator.LinearClassifier`\n",
        "\n",
        "Iris 문제의 경우 `tf.estimator.DNNClassifier`가 최선의 선택인 것으로 여겨집니다. 이 Estimator를 인스턴스화하는 방법은 다음과 같습니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "qnf4o2V5lcPn"
      },
      "outputs": [],
      "source": [
        "# Build a DNN with 2 hidden layers with 30 and 10 hidden nodes each.\n",
        "classifier = tf.estimator.DNNClassifier(\n",
        "    feature_columns=my_feature_columns,\n",
        "    # Two hidden layers of 30 and 10 nodes respectively.\n",
        "    hidden_units=[30, 10],\n",
        "    # The model must choose between 3 classes.\n",
        "    n_classes=3)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "tzzt5nUpmEe3"
      },
      "source": [
        "## 훈련, 평가 및 예측하기\n",
        "\n",
        "이제 Estimator 객체가 준비되었으므로 메서드를 호출하여 다음을 수행할 수 있습니다.\n",
        "\n",
        "- 모델을 훈련합니다.\n",
        "- 훈련한 모델을 평가합니다.\n",
        "- 훈련한 모델을 사용하여 예측을 수행합니다."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "rnihuLdWmE75"
      },
      "source": [
        "### 모델 훈련하기\n",
        "\n",
        "다음과 같이 Estimator의 `train` 메서드를 호출하여 모델을 훈련합니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "4jW08YtPl1iS"
      },
      "outputs": [],
      "source": [
        "# Train the Model.\n",
        "classifier.train(\n",
        "    input_fn=lambda: input_fn(train, train_y, training=True),\n",
        "    steps=5000)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "ybiTFDmlmes8"
      },
      "source": [
        "Estimator가 예상한 대로 인수를 사용하지 않는 입력 함수를 제공하면서 인수를 포착하기 위해 [`lambda`](https://docs.python.org/3/tutorial/controlflow.html)에서 `input_fn` 호출을 래핑합니다. `steps` 인수는 여러 훈련 단계를 거친 후에 훈련을 중지하도록 메서드에 지시합니다.\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "HNvJLH8hmsdf"
      },
      "source": [
        "### 훈련한 모델 평가하기\n",
        "\n",
        "모델을 훈련했으므로 성능에 대한 통계를 얻을 수 있습니다. 다음 코드 블록은 테스트 데이터에서 훈련한 모델의 정확도를 평가합니다.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "A169XuO4mKxF"
      },
      "outputs": [],
      "source": [
        "eval_result = classifier.evaluate(\n",
        "    input_fn=lambda: input_fn(test, test_y, training=False))\n",
        "\n",
        "print('\\nTest set accuracy: {accuracy:0.3f}\\n'.format(**eval_result))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "VnPMP5EHph17"
      },
      "source": [
        "`train` 메서드에 대한 호출과 달리 평가할 `steps` 인수를 전달하지 않았습니다. eval에 대한 `input_fn`은 단 하나의 데이터 [epoch](https://developers.google.com/machine-learning/glossary/#epoch)만 생성합니다.\n",
        "\n",
        "`eval_result` 사전에는 `average_loss`(샘플당 평균 손실), `loss`(미니 배치당 평균 손실) 및 Estimator의 `global_step` 값(받은 훈련 반복 횟수)도 포함됩니다.\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "ur624ibpp52X"
      },
      "source": [
        "### 훈련한 모델에서 예측(추론)하기\n",
        "\n",
        "우수한 평가 결과를 생성하는 훈련한 모델을 만들었습니다. 이제 훈련한 모델을 사용하여 레이블이 지정되지 않은 일부 측정을 바탕으로 아이리스 꽃의 종을 예측할 수 있습니다. 훈련 및 평가와 마찬가지로 단일 함수 호출을 사용하여 예측합니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "wltc0jpgng38"
      },
      "outputs": [],
      "source": [
        "# Generate predictions from the model\n",
        "expected = ['Setosa', 'Versicolor', 'Virginica']\n",
        "predict_x = {\n",
        "    'SepalLength': [5.1, 5.9, 6.9],\n",
        "    'SepalWidth': [3.3, 3.0, 3.1],\n",
        "    'PetalLength': [1.7, 4.2, 5.4],\n",
        "    'PetalWidth': [0.5, 1.5, 2.1],\n",
        "}\n",
        "\n",
        "def input_fn(features, batch_size=256):\n",
        "    \"\"\"An input function for prediction.\"\"\"\n",
        "    # Convert the inputs to a Dataset without labels.\n",
        "    return tf.data.Dataset.from_tensor_slices(dict(features)).batch(batch_size)\n",
        "\n",
        "predictions = classifier.predict(\n",
        "    input_fn=lambda: input_fn(predict_x))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "JsETKQo0rHvi"
      },
      "source": [
        "`predict` 메서드는 Python iterable을 반환하여 각 예제에 대한 예측 결과 사전을 생성합니다. 다음 코드는 몇 가지 예측과 해당 확률을 출력합니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "Efm4mLzkrCxp"
      },
      "outputs": [],
      "source": [
        "for pred_dict, expec in zip(predictions, expected):\n",
        "    class_id = pred_dict['class_ids'][0]\n",
        "    probability = pred_dict['probabilities'][class_id]\n",
        "\n",
        "    print('Prediction is \"{}\" ({:.1f}%), expected \"{}\"'.format(\n",
        "        SPECIES[class_id], 100 * probability, expec))"
      ]
    }
  ],
  "metadata": {
    "colab": {
      "collapsed_sections": [],
      "name": "premade.ipynb",
      "toc_visible": true
    },
    "kernelspec": {
      "display_name": "Python 3",
      "name": "python3"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}
